package com.lazydong.apiforward.gateway.component;

import com.lazydong.apiforward.gateway.model.RouteDefinitionInfo;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.*;

/**
 * 动态路由组件
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class GatewayDynamicRouteComponent implements ApplicationEventPublisherAware, SmartInitializingSingleton {
    private final RouteDefinitionWriter routeDefinitionWriter;

    @Autowired(required = false)
    private List<GatewayDynamicRoute> gatewayDynamicRoutes;

    private ApplicationEventPublisher applicationEventPublisher;

    private final Set<String> serviceIdSet = new HashSet<>();

    public GatewayDynamicRouteComponent(RouteDefinitionWriter routeDefinitionWriter) {
        this.routeDefinitionWriter = routeDefinitionWriter;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public void afterSingletonsInstantiated() {
        ServiceLoader<GatewayDynamicRoute> dynamicRoutes = ServiceLoader.load(GatewayDynamicRoute.class);
        dynamicRoutes.forEach(temp -> temp.setDynamicRouteComponent(GatewayDynamicRouteComponent.this));
        if (gatewayDynamicRoutes != null) {
            gatewayDynamicRoutes.forEach(temp -> temp.setDynamicRouteComponent(GatewayDynamicRouteComponent.this));
        }
    }


    public void routeChangeEvent(RouteDefinitionInfo routeDefinitionInfo) {
        String url = routeDefinitionInfo.getUrl();
        if (url == null || url.trim().isEmpty()) {
            deleteRoute(routeDefinitionInfo.getServiceId());
            return;
        }
        RouteDefinition routeDefinition = serviceIpAddr2RouteDefinition(routeDefinitionInfo);
        if (serviceIdSet.contains(routeDefinition.getId())) {
            updateRoute(routeDefinition);
            return;
        }
        addRoute(routeDefinition);
    }

    private void addRoute(RouteDefinition routeDefinition) {
        if (serviceIdSet.contains(routeDefinition.getId())) {
            return;
        }
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        serviceIdSet.add(routeDefinition.getId());
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
        System.out.println("addRoute 成功");
    }

    private void deleteRoute(String id) {
        if (!serviceIdSet.contains(id)) {
            return;
        }
        routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        serviceIdSet.remove(id);
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
        System.out.println("deleteRoute 成功");
    }

    private void updateRoute(RouteDefinition routeDefinition) {
        if (!serviceIdSet.contains(routeDefinition.getId())) {
            return;
        }
        this.routeDefinitionWriter.delete(Mono.just(routeDefinition.getId())).subscribe();
        routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
        applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
        System.out.println("updateRoute 成功");
    }

    private RouteDefinition serviceIpAddr2RouteDefinition(RouteDefinitionInfo routeDefinitionInfo) {
        RouteDefinition routeDefinitionBase = new RouteDefinition();
        routeDefinitionBase.setId(routeDefinitionInfo.getServiceId());
        routeDefinitionBase.setUri(URI.create(routeDefinitionInfo.getUrl()));
        routeDefinitionBase.setPredicates(new ArrayList<>() {{
            add(new PredicateDefinition("Path=" + routeDefinitionInfo.getPredicate()));
        }});
        List<FilterDefinition> filters = getFilterDefinitions(routeDefinitionInfo);
        if (!filters.isEmpty()) {
            routeDefinitionBase.setFilters(filters);
        }
        if (routeDefinitionInfo.getOrdered() != null) {
            // 值越大，越是最后匹配
            routeDefinitionBase.setOrder(routeDefinitionInfo.getOrdered());
        }
        return routeDefinitionBase;
    }

    private static List<FilterDefinition> getFilterDefinitions(RouteDefinitionInfo routeDefinitionInfo) {
        List<FilterDefinition> filters = new ArrayList<>();
        if (routeDefinitionInfo.getStripPrefix() != null) {
            // 使用 StripPrefix 过滤器工厂
            FilterDefinition filterDefinition = new FilterDefinition("StripPrefix=" + routeDefinitionInfo.getStripPrefix());
            filters.add(filterDefinition);
        }
        if (routeDefinitionInfo.getRegexp() != null && !routeDefinitionInfo.getRegexp().isEmpty()) {
            // 使用 RewritePath 替换路径
            FilterDefinition filterDefinition = new FilterDefinition("RewritePath=" + routeDefinitionInfo.getRegexp() + "," + routeDefinitionInfo.getReplacement());
            filters.add(filterDefinition);
        }
        return filters;
    }
}
